using System;
using System.Reflection;
using System.Text;

namespace WindowsPhone.CompactContainer.ServiceLocation.CompactContainer
{
    /// <summary>
    /// The default handler
    /// </summary>
    public class DefaultHandler : AbstractHandler
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultHandler"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        public DefaultHandler(IContainer container)
        {
            Container = container;
        }

        /// <summary>
        /// Creates the specified class type.
        /// </summary>
        /// <param name="classType">Type of the class.</param>
        /// <returns></returns>
        public override object Create(Type classType)
        {
            ConstructorInfo[] constructors = classType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);

            if (constructors.Length < 1)
            {
                throw new ContainerException("Type " + classType.Name + " must have at least one public constructor");
            }
            else if (constructors.Length > 1)
            {
                throw new ContainerException("Type " + classType.Name + " must only have one public constructor");
            }

            ConstructorInfo theConstructor = null;
            object[] theConstructorParameters = new object[0];
            StringBuilder missingComponents = new StringBuilder();

            try
            {
                foreach (ConstructorInfo constructorInfo in constructors)
                {
                    ParameterInfo[] parameters = constructorInfo.GetParameters();

                    if (parameters.Length > theConstructorParameters.Length
                        || theConstructor == null)
                    {
                        bool proposeNewConstructor = true;
                        object[] parameterObjects = new object[parameters.Length];

                        for (int i = 0; i < parameters.Length; i++)
                        {
                            if (Container.HasComponent(parameters[i].ParameterType))
                            {
                                parameterObjects[i] = ResolveConstructorDependency(classType, parameters[i].ParameterType);
                            }
                            else
                            {
                                missingComponents.Append(parameters[i].ParameterType.Name + "; ");
                                proposeNewConstructor = false;
                                break;
                            }
                        }

                        if (proposeNewConstructor)
                        {
                            theConstructor = constructorInfo;
                            theConstructorParameters = parameterObjects;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ContainerException("Error while evaluating dependencies of " + classType.Name, ex);
            }

          

            if (theConstructor == null)
            {
                throw new ContainerException("Missing components: " + missingComponents + " required by " + classType.Name);
            }

            try
            {
                return theConstructor.Invoke(theConstructorParameters);
            }
            catch (Exception ex)
            {
                throw new ContainerException("Error while invoking a constructor of " + classType.Name, ex);
            }
           
        }

        /// <summary>
        /// Resolves the constructor dependency.
        /// </summary>
        /// <param name="classType">Type of the class.</param>
        /// <param name="parameterType">Type of the parameter.</param>
        /// <returns></returns>
        protected virtual object ResolveConstructorDependency(Type classType, Type parameterType)
        {
            return Container.Resolve(parameterType);
        }
    }
}